
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;
using Swaf.Security;

namespace Reservations
{
	public class RoomAvailabilityBizRules : Syrinx.BaseBizRules
	{
		public void setupProductAvailableQuantityForUser(IApplication app, IRuleContext context, IBizObj roomTypeProduct)
		{
			IBizObjList userRoomAvailability = context.get("userRoomAvailability") as IBizObjList;
			if (userRoomAvailability == null)
			{
				IBizObj currentSalesOrder = context.get("currentSalesOrder") as IBizObj;
				if (currentSalesOrder == null)
					context.put("currentSalesOrder", currentSalesOrder = context.RuleGroup.execute(context, "getCurrentSalesOrderDetails").CallResults as IBizObj);
				if (currentSalesOrder != null)
				{
					object arrivalDate = currentSalesOrder.get("reservation.arrivalDate");
					object departureDate = currentSalesOrder.get("reservation.departureDate");
					userRoomAvailability = context.RuleGroup.execute(context, "getUserRoomAvailability",
						arrivalDate, departureDate).CallResults as IBizObjList;
					context.put("userRoomAvailability", userRoomAvailability);
				}
				else
					roomTypeProduct.put("quantityAvailable", -2);
			}

			if (userRoomAvailability != null)
			{
				IBizObj roomTypeAvailability = userRoomAvailability.search("name", roomTypeProduct.get("partId"), false);
				if (roomTypeAvailability != null)
				{
					int quantity = (int)roomTypeAvailability.get("quantityAvailable");
					if (quantity < 0)
						quantity = 0;
					roomTypeProduct.put("quantityAvailable", quantity);
				}
				else
				{
					//TODO:log
				}
			}
		}

		public string[] isRoomAvailableToUser(IApplication app, IRuleContext context,
			DateTime arrivalDate, DateTime departureDate, string roomTypePartId, int quantity)
		{
			string[] availabilityInfo = null;

			Swaf.BGuid.Guid userId = null;
			bool isBookingAgent = false;
			if (isBookingAgent = CurrentPrincipal.isUserInRole("BookingAgents"))
				userId = (Swaf.BGuid.Guid)CurrentUser.get("id");
			else
				userId = (Swaf.BGuid.Guid)ActiveCompany.get("anonymousId");

			context.put("fromDate", arrivalDate);
			context.put("toDate", departureDate);
			context.put("userId", userId);
			IBizObjList defaultAllocations = app.appData.get("DA.Reservations.GetBookingAgentRoomTypeDefaultAllocations", context) as IBizObjList;
			IBizObjList allocationExceptions = app.appData.get("DA.Reservations.GetBookingAgentAllocationExceptionForPeriod", context) as IBizObjList;
			IBizObjList agentResItems = (isBookingAgent ?
				app.appData.get("DA.Reservations.GetBookingAgentUserRoomTypeReservationItems", context) :
				app.appData.get("DA.Reservations.GetGenIntUserRoomTypeReservationItems", context)) as IBizObjList;

			TimeSpan span = departureDate - arrivalDate;
			int numDaysStay = span.Days;

			IBizObj defaultAllocationForRoom = defaultAllocations.search("name", roomTypePartId, false);
			for (int pos = 0; pos < numDaysStay; pos++)
			{
				DateTime day = arrivalDate.AddDays(pos);
				int givenAlloc = -1;

				//Find exception that covers date
				foreach (IBizObj exc in allocationExceptions)
					if ((DateTime)exc.get("fromDate") <= day && (DateTime)exc.get("toDate") >= day &&
						(roomTypePartId.CompareTo(exc.get("roomTypeProductId")) == 0))
					{
						givenAlloc = (int)exc.get("quantity");
						break;
					}
				if (givenAlloc == -1)
					if (defaultAllocationForRoom == null)
						givenAlloc = 0;
					else
						givenAlloc = (int)defaultAllocationForRoom.get("quantity");

				//Find reservations that cover the day
				int numUsed = 0;
				foreach (IBizObj resItem in agentResItems)
					if ((DateTime)resItem.get("arrivalDate") <= day &&
						(DateTime)resItem.get("departureDate") > day && roomTypePartId.Equals(resItem.get("partId")))
						numUsed += (int)resItem.get("quantity");

				int quantityAvailable = givenAlloc - numUsed;
				if (quantityAvailable < quantity)
				{
					availabilityInfo = new string[] {(arrivalDate + new TimeSpan(pos, 0, 0, 0, 0)).ToString("D"),
									quantityAvailable.ToString()};
					break;
				}
			}

			return availabilityInfo;
		}

		public IBizObjList getUserRoomAvailability(IApplication app, IRuleContext context,
			DateTime arrivalDate, DateTime departureDate)
		{
			IBizObjList rooms = app.bizObjMgr.createList("RoomTypeAllocation");
			Swaf.BGuid.Guid userId = null;
			bool isBookingAgent = false;
			if (isBookingAgent = CurrentPrincipal.isUserInRole("BookingAgents"))
				userId = (Swaf.BGuid.Guid)CurrentUser.get("id");
			else
				userId = (Swaf.BGuid.Guid)ActiveCompany.get("anonymousId");

			context.put("fromDate", arrivalDate);
			context.put("toDate", departureDate);
			context.put("userId", userId);
			IBizObjList defaultAllocations = app.appData.get("DA.Reservations.GetBookingAgentRoomTypeDefaultAllocations", context) as IBizObjList;
			IBizObjList allocationExceptions = app.appData.get("DA.Reservations.GetBookingAgentAllocationExceptionForPeriod", context) as IBizObjList;
			IBizObjList roomTypes = context.RuleGroup.execute(context, "getRoomTypes").CallResults as IBizObjList;
			IBizObjList agentResItems = (isBookingAgent?
				app.appData.get("DA.Reservations.GetBookingAgentUserRoomTypeReservationItems", context):
				app.appData.get("DA.Reservations.GetGenIntUserRoomTypeReservationItems", context)) as IBizObjList;
			if (roomTypes != null)
			{
				TimeSpan span = departureDate - arrivalDate;
				int numDaysStay = span.Days;
				foreach (IBizObj roomType in roomTypes)
				{
					IBizObj allocation = rooms.insertNew();
					allocation.put("name", roomType.get("partId"));

					IBizObj defaultAllocationForRoom = defaultAllocations.search("name", allocation.get("name"), false);
					IBizObjList dailyAllocations = app.bizObjMgr.createList("RoomTypeAllocation");
					for (int pos = 0; pos < numDaysStay; pos++)
					{
						IBizObj dayAllocation = dailyAllocations.insertNew();
						DateTime day = arrivalDate.AddDays(pos);
						int givenAlloc = -1;
						dayAllocation.put("date", day);

						//Find exception that covers date
						foreach(IBizObj exc in allocationExceptions)
							
							if ((DateTime)exc.get("fromDate") <= day && (DateTime)exc.get("toDate") >= day &&
								((IComparable)exc.get("roomTypeProductId")).CompareTo(roomType.get("partId")) == 0)
							{
								dayAllocation.put("quantity", givenAlloc = (int)exc.get("quantity"));
								break;
							}
						if (givenAlloc == -1)
							if (defaultAllocationForRoom == null)
								dayAllocation.put("quantity", givenAlloc = 0);
							else
								dayAllocation.put("quantity", defaultAllocationForRoom.get("quantity"));

						//Find reservations that cover the day
						int numUsed = 0;
						foreach (IBizObj resItem in agentResItems)
							if ((DateTime)resItem.get("arrivalDate") <= day &&
								(DateTime)resItem.get("departureDate") > day && roomType.get("partId").Equals(resItem.get("partId")))
								numUsed += (int)resItem.get("quantity");

						dayAllocation.put("quantityUsed", numUsed);
						dayAllocation.put("quantityAvailable", (int)dayAllocation.get("quantity") - numUsed);

						if (!allocation.isDirty("quantityAvailable") || 
							(int)dayAllocation.get("quantityAvailable") < (int)allocation.get("quantityAvailable"))
						{
							allocation.put("quantityAvailable", dayAllocation.get("quantityAvailable"));
							allocation.put("quantityUsed", numUsed);
							allocation.put("quantity", dayAllocation.get("quantity"));
						}
					}
				}
			}

			return rooms;
		}


		public List<Swaf.Gui.UserMessage> validateReservationRoomQuantity(IApplication app, IRuleContext context, object resultValue, object[] inputParams)
		{
			List<Swaf.Gui.UserMessage> msgs = null;
			IBizObj salesOrder = (IBizObj) app.ruleMgr.execute("getCurrentSalesOrderDetails").CallResults;
			if (salesOrder != null)
			{
				IBizObj reservation = (IBizObj)salesOrder.get("reservation");
				msgs = (List<Swaf.Gui.UserMessage>)resultValue;
				IBizObj item = (IBizObj)inputParams[2];
				if (((string)item.get("product.partId")).StartsWith("RT-"))
				{
					IResults rc = context.RuleGroup.execute(context, "isRoomAvailableToUser",
								reservation.get("arrivalDate"), reservation.get("departureDate"), item.get("product.partId"), item.get("quantity"));
					string[] availabilityInfo = (string[])rc.CallResults;
					if (availabilityInfo != null)
					{
						FlexiMap m = new FlexiMap();
						if (msgs == null)
							msgs = new List<Swaf.Gui.UserMessage>();
						m.put("roomTitle", item.get("product.title"));
						m.put("reasonDate", availabilityInfo[0]);
						m.put("quantityAvailable", availabilityInfo[1]);
						msgs.Add(new Swaf.Gui.UserMessage("Error-RoomNotAvailabeOnDesiredDate", m));
					}
				}
			}
			return msgs;
		}

	}
}
